//
//  NSObjectAdditions.m
//  Tint
//
//  Created by Kevin Wu on 2/22/12.
//  Copyright (c) 2012 Telligenty. All rights reserved.
//

#import "NSObjectAdditions.h"
#import <objc/runtime.h>


@implementation NSObject (Tint)


#pragma mark - Class string

+ (NSString *)classString
{
    return NSStringFromClass([self class]);
}

- (NSString *)classString
{
    return NSStringFromClass([self class]);
}



#pragma mark - Associate object

- (void)associateValue:(id)value withKey:(void *)key
{
	objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN);
}

- (void)weaklyAssociateValue:(id)value withKey:(void *)key
{
	objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_ASSIGN);
}

- (id)associatedValueForKey:(void *)key
{
	return objc_getAssociatedObject(self, key);
}


#pragma mark - KVC

- (BOOL)isValueForKeyPath:(NSString *)keyPath equalToValue:(id)value
{
    return [[self valueForKeyPath:keyPath] isEqual:value];
}

- (BOOL)isValueForKeyPath:(NSString *)keyPath identicalToValue:(id)value
{
    return ( [self valueForKeyPath:keyPath] == value );
}

+ (NSArray *)newPropertyNames
{
    NSMutableArray *names = nil;
    
    unsigned int count = 0;
    objc_property_t *properties = class_copyPropertyList(self, &count);
    if ( count > 0 ) {
        names = [[NSMutableArray alloc] init];
        for ( int i=0; i<count; ++i ) {
            NSString *name = [[NSString alloc] initWithUTF8String:property_getName(properties[i])];
            [names addObject:name];
            [name release];
        }
    }
    free(properties);
    
    return names;
}

+ (NSDictionary *)newPropertyAttributes
{
    NSMutableDictionary *attributes = nil;
    
    unsigned int count = 0;
    objc_property_t *properties = class_copyPropertyList(self, &count);
    if ( count > 0 ) {
        attributes = [[NSMutableDictionary alloc] init];
        for ( int i=0; i<count; ++i ) {
            objc_property_t property = properties[i];
            NSString *name = [[NSString alloc] initWithUTF8String:property_getName(property)];
            NSString *attribute = [[NSString alloc] initWithUTF8String:property_getAttributes(property)];
            [attributes setObject:attribute forKey:name];
            [name release];
            [attribute release];
        }
    }
    free(properties);
    
    return attributes;
}



#pragma mark - Sending messages

- (id)runSelector:(SEL)selector
      withObjects:(NSArray *)objects
     onMainThread:(BOOL)on
{
    if ( ![self respondsToSelector:selector] ) {
        return nil;
    }
    
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
    if ( signature ) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self];
        [invocation setSelector:selector];
        
        int j = 2;
        for ( int i=0; i<[objects count]; ++i ) {
            id object = [objects objectAtIndex:i];
            [invocation setArgument:&object atIndex:j];
            ++j;
        }
        
        if ( on ) {
            [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];
        } else {
            [invocation invoke];
        }
        
        if ( [signature methodReturnLength] ) {
            id ret = nil;
            [invocation getReturnValue:&ret];
            return ret;
        }
    }
    return nil;
}

- (id)runSelector:(SEL)selector
     onMainThread:(BOOL)on
{
    if ( ![self respondsToSelector:selector] ) {
        return nil;
    }
    
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
    if ( signature ) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self];
        [invocation setSelector:selector];
        
        if ( on ) {
            [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];
        } else {
            [invocation invoke];
        }
        
        if ( [signature methodReturnLength] ) {
            id ret = nil;
            [invocation getReturnValue:&ret];
            return ret;
        }
    }
    return nil;
}

- (id)runSelector:(SEL)selector
       withObject:(id)p1
     onMainThread:(BOOL)on
{
    if ( ![self respondsToSelector:selector] ) {
        return nil;
    }
    
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
    if ( signature ) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self];
        [invocation setSelector:selector];
        
        [invocation setArgument:&p1 atIndex:2];
        
        if ( on ) {
            [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];
        } else {
            [invocation invoke];
        }
        
        if ( [signature methodReturnLength] ) {
            id ret = nil;
            [invocation getReturnValue:&ret];
            return ret;
        }
    }
    return nil;
}

- (id)runSelector:(SEL)selector
       withObject:(id)p1
       withObject:(id)p2
     onMainThread:(BOOL)on
{
    if ( ![self respondsToSelector:selector] ) {
        return nil;
    }
    
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
    if ( signature ) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self];
        [invocation setSelector:selector];
        
        [invocation setArgument:&p1 atIndex:2];
        [invocation setArgument:&p2 atIndex:3];
        
        if ( on ) {
            [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];
        } else {
            [invocation invoke];
        }
        
        if ( [signature methodReturnLength] ) {
            id ret = nil;
            [invocation getReturnValue:&ret];
            return ret;
        }
    }
    return nil;
}

- (id)runSelector:(SEL)selector
       withObject:(id)p1
       withObject:(id)p2
       withObject:(id)p3
     onMainThread:(BOOL)on
{
    if ( ![self respondsToSelector:selector] ) {
        return nil;
    }
    
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
    if ( signature ) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self];
        [invocation setSelector:selector];
        
        [invocation setArgument:&p1 atIndex:2];
        [invocation setArgument:&p2 atIndex:3];
        [invocation setArgument:&p3 atIndex:4];
        
        if ( on ) {
            [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];
        } else {
            [invocation invoke];
        }
        
        if ( [signature methodReturnLength] ) {
            id ret = nil;
            [invocation getReturnValue:&ret];
            return ret;
        }
    }
    return nil;
}

- (id)runSelector:(SEL)selector
       withObject:(id)p1
       withObject:(id)p2
       withObject:(id)p3
       withObject:(id)p4
     onMainThread:(BOOL)on
{
    if ( ![self respondsToSelector:selector] ) {
        return nil;
    }
    
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
    if ( signature ) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self];
        [invocation setSelector:selector];
        
        [invocation setArgument:&p1 atIndex:2];
        [invocation setArgument:&p2 atIndex:3];
        [invocation setArgument:&p3 atIndex:4];
        [invocation setArgument:&p4 atIndex:5];
        
        if ( on ) {
            [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];
        } else {
            [invocation invoke];
        }
        
        if ( [signature methodReturnLength] ) {
            id ret = nil;
            [invocation getReturnValue:&ret];
            return ret;
        }
    }
    return nil;
}

- (id)runSelector:(SEL)selector
       withObject:(id)p1
       withObject:(id)p2
       withObject:(id)p3
       withObject:(id)p4
       withObject:(id)p5
     onMainThread:(BOOL)on
{
    if ( ![self respondsToSelector:selector] ) {
        return nil;
    }
    
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
    if ( signature ) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self];
        [invocation setSelector:selector];
        
        [invocation setArgument:&p1 atIndex:2];
        [invocation setArgument:&p2 atIndex:3];
        [invocation setArgument:&p3 atIndex:4];
        [invocation setArgument:&p4 atIndex:5];
        [invocation setArgument:&p5 atIndex:6];
        
        if ( on ) {
            [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];
        } else {
            [invocation invoke];
        }
        
        if ( [signature methodReturnLength] ) {
            id ret = nil;
            [invocation getReturnValue:&ret];
            return ret;
        }
    }
    return nil;
}

- (id)runSelector:(SEL)selector
       withObject:(id)p1
       withObject:(id)p2
       withObject:(id)p3
       withObject:(id)p4
       withObject:(id)p5
       withObject:(id)p6
     onMainThread:(BOOL)on
{
    if ( ![self respondsToSelector:selector] ) {
        return nil;
    }
    
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
    if ( signature ) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        [invocation setTarget:self];
        [invocation setSelector:selector];
        
        [invocation setArgument:&p1 atIndex:2];
        [invocation setArgument:&p2 atIndex:3];
        [invocation setArgument:&p3 atIndex:4];
        [invocation setArgument:&p4 atIndex:5];
        [invocation setArgument:&p5 atIndex:6];
        [invocation setArgument:&p6 atIndex:7];
        
        if ( on ) {
            [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];
        } else {
            [invocation invoke];
        }
        
        if ( [signature methodReturnLength] ) {
            id ret = nil;
            [invocation getReturnValue:&ret];
            return ret;
        }
    }
    return nil;
}

@end
